#include <linux/module.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include "pwm.h"


#define TCFG0	0x00
#define TCFG1	0x04
#define TCON	0x08
#define TCNTB1	0x0C
#define TCMPB1	0x10

#define GPDCON 		0x114000A0
#define TIMER_BASE 	0x139D0000


typedemkf struct pwm_desc_t{
    unsigned int *gpdcon;
    void __iomem *timer_base;
    unsigned int major;
    struct class *cls;
    struct device *dev;
    
    
}pwm_desc;  

pwm_desc *pwm;


static int pwm_open(struct inode *inode, struct file *file)
{
	writel((readl(pwm->gpdcon) & ~0xf) | 0x2, pwm->gpdcon);
	writel(readl(pwm->timer_base + TCFG0) | 0xff, pwm->timer_base + TCFG0);
	writel((readl(pwm->timer_base + TCFG1) & ~0xf) | 0x2, pwm->timer_base + TCFG1);
	writel(300, pwm->timer_base + TCNTB1);
	writel(150, pwm->timer_base + TCMPB1);
	writel((readl(pwm->timer_base + TCON) & ~0x1f) | 0x2, pwm->timer_base + TCON);
	//writel((readl(pwm->timer_base + TCON) & ~(0xf << 8)) | (0x9 << 8), pwm->timer_base + TCON);
	return 0;
}

static int pwm_rlease(struct inode *inode, struct file *file)
{
	writel(readl(pwm->timer_base + TCON) & ~0xf, pwm->timer_base + TCON);
	return 0;
}

static long pwm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{

	switch(cmd)
	{
	case PWM_ON:
		writel((readl(pwm->timer_base + TCON) & ~0x1f) | 0x9, pwm->timer_base + TCON);
		break;
	case PWM_OFF:
		writel(readl(pwm->timer_base + TCON) & ~0x1f, pwm->timer_base + TCON);
		break;
	case SET_PRE:
		writel(readl(pwm->timer_base + TCON) & ~0x1f, pwm->timer_base + TCON);
		writel((readl(pwm->timer_base + TCFG0) & ~0xff) | (0xff & 0xff), pwm->timer_base + TCFG0);
		writel((readl(pwm->timer_base + TCON) & ~0x1f) | 0x9, pwm->timer_base + TCON);
		break;
	case SET_CNT:
		writel(0xff, pwm->timer_base + TCNTB1);
		writel(0xff >> 1, pwm->timer_base + TCMPB1);
		break;
	}

	return 0;
}
	
struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = pwm_open,
	.release = pwm_rlease,
	.unlocked_ioctl = pwm_ioctl,
};


int pwm_init(void)
{
        pwm = kzalloc(sizeof(pwm_desc), GFP_KERNEL);
        
      
       pwm->major = register_chrdev(0, "pwm", &fops);
       if(pwm->major <= 0){
            printk("register chrdev error\n");
            goto err0;
       }
        pwm->cls = class_create(THIS_MODULE, "pwm0");
        if(!pwm->cls){
                 printk("class_create error\n");       
                 goto err1;
        }
        pwm->dev = device_create(pwm->cls, NULL, MKDEV(pwm->major, 0), NULL, "pwm");
          if(!pwm->dev){
                 printk("device_create error\n");       
                 goto err2;
        }     

         pwm->gpdcon = ioremap(GPDCON, 4);
         pwm->timer_base = ioremap(TIMER_BASE, 0x20);
         
        return 0;
err2:
        class_destroy(pwm->cls);
err1:
        unregister_chrdev(pwm->major, "pwm");
err0:
    
        kfree(pwm);
        return -1;
}

void pwm_exit(void)
{
        device_destroy(pwm->cls, MKDEV(pwm->major, 0));
        class_destroy(pwm->cls);
        unregister_chrdev(pwm->major, "pwm");
        iounmap(pwm->gpdcon);
        kfree(pwm);

}

module_init(pwm_init);

module_exit(pwm_exit);

MODULE_LICENSE("GPL");
